﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.SqlCommand;
using Lib.Query;
using System.Collections;

namespace Lib.Query.NHibernate
{
    public static class NHibernateQueryTranslator
    {
        public static ICriteria Translate<T>(this IReadLiteQuery query, ICriteria criteria)
        {                        
            if (query.Criteria != null)
            {                
                foreach (string alias in GetAliases<T>(query))
                {               
                    criteria.CreateAlias(alias, alias);
                }

                IList<ICriterion> critrions = new List<ICriterion>();                

                foreach (Criteria c in query.Criteria)
                {                                             
                    switch (c.criteriaOperator)
                    {
                        case CriteriaOperator.Equal:
                            critrions.Add(Property.ForName(c.PropertyName).Eq(c.Value));
                            break;
                        case CriteriaOperator.GreaterThan:
                            critrions.Add(Property.ForName(c.PropertyName).Gt(c.Value));
                            break;
                        case CriteriaOperator.GreaterThanEqual:
                            critrions.Add(Property.ForName(c.PropertyName).Ge(c.Value));
                            break;
                        case CriteriaOperator.LesserThan:
                            critrions.Add(Property.ForName(c.PropertyName).Lt(c.Value));
                            break;
                        case CriteriaOperator.LesserThanEqual:
                            critrions.Add(Property.ForName(c.PropertyName).Le(c.Value));
                            break;
                        case CriteriaOperator.Like:
                            critrions.Add(Property.ForName(c.PropertyName).Like(c.Value + "%"));
                            break;
                        case CriteriaOperator.NotEqual:
                            critrions.Add(Expression.Not(Property.ForName(c.PropertyName).Eq(c.Value)));
                            break;                        
                        default:
                            throw new ApplicationException("No operator defined");
                    }                 
                }

                if (query.QueryOperator == QueryOperator.And)
                {
                    Conjunction andSubQuery = Expression.Conjunction();
                    foreach (ICriterion criterion in critrions)
                    {
                        andSubQuery.Add(criterion);
                    }
                    criteria.Add(andSubQuery);
                }
                else
                {
                    Disjunction orSubQuery = Expression.Disjunction();
                    foreach (ICriterion criterion in critrions)
                    {
                        orSubQuery.Add(criterion);
                    }
                    criteria.Add(orSubQuery);
                }
            }                        

            if(query.OrderByClause != null)
                criteria.AddOrder(new Order(query.OrderByClause.PropertyName, query.OrderByClause.Ascending));

            if (query.TopN > 0)
                criteria.SetMaxResults(query.TopN);
          
            return criteria;
        }

        public static IList<string> GetAliases<T>(IReadLiteQuery query)
        {
            IList<string> aliases = new List<string>();
            string alias = null;
            foreach (Criteria c in query.Criteria)
            {
                alias = GetAlias(c.PropertyName);
                if (alias != null && !aliases.Contains(alias))
                    aliases.Add(alias);                    
            }

            if (query.OrderByClause != null && !string.IsNullOrWhiteSpace(query.OrderByClause.PropertyName))
            {
                alias = GetAlias(query.OrderByClause.PropertyName);
                if (alias != null && !aliases.Contains(alias))
                    aliases.Add(alias);
            }

            return aliases;
        }

        public static string GetAlias(string propertyName)
        {
            string alias = null;
            bool hasAlias = (propertyName.IndexOf(".") != -1);
            if (hasAlias)
                alias = propertyName.Substring(0, propertyName.IndexOf("."));

            return alias;
        }
    }    
}
